/* Autostop
*
* Copyright (c) Maciej Gajewski, Grzegorz Latarowski
* http://code.google.com/p/autostop-automaty-drogowe
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version. NO WARRANTY OF ANY KIND is expressed or implied.
*/

///\file gnetudp.h UPD sending/rcieving tools


#ifndef GNETUDP_H
#define GNETUDP_H


// Qt
#include <QUdpSocket>
#include <QDateTime>
#include <QMap>
#include <QTimer>
#include <QVector>
#include <QQueue>
#include <QPair>

/// Header added to each datagram
struct _GUdpDatagramHeader
{
	quint16 blockId;			///< unique block ID
	// TODO this is propably not needed
	//quint32 blockSize;			///< block size [bytes]
	quint16 datagramNumber;		///< Ordinal number of datagram in block
	quint16	datagramsInBlock;	///< Total number of datagarams in this block
	
	// then follows data, up to datagram size 
	
} __attribute__((packed));


/// UDP Sender - tool sending packets of any size via UDP
/// It accepts data bytes of any size, splits it into smaller chunks,
/// and sends these chuncks via UPD. There is a tiny header added to each chunk
/// that lets GUdpReciever to get these chunks together at the other end
class GUdpSender : public QObject
{
	Q_OBJECT
public:

	// construction / destruction
	
	GUdpSender( QObject* parent = 0 );
	virtual ~GUdpSender();
	
	// data sending
	
	/// Sends data synchronously
	void sendSync( const QByteArray& data, const QHostAddress & address, quint16 port );
	
	/// Sends data asynchronously (returns immediately)
	void sendAsync( const QByteArray& data, const QHostAddress & address, quint16 port );
	
	// configuration
	
	void setDatagramSize( int size ) { _datagramSize = size; }
	int getDatagramSize() const { return _datagramSize; }
	
private:

	// data
	
	int _datagramSize;				///< Maximum acceptable datagram size [bytes]
	QUdpSocket	_socket;			///< Socket
	int _datagramId;				///< Next datagram id (running counter)
};

/// UDP Reciever - asynchronous receiver for packets send by GUdpSender.
/// This class provides receiver for packets sent by GUdpSender. It uses 
/// event loop in current thread, so it os suitable for GUI application's main threads.
/// Whenever completed packet is received, the packetReceived() signal is emited. 
///
/// It may work without event loop. You just need to call receiveDatagrams() once in a while.
///
/// The receiver may also work in 'congestion control' mode, which is useful in case when 
/// packets arrive faster than they are processed. In this mode, it emits packetsReceived()
/// signal instead of packetReceived(). The signal provides entire batch of packets
/// recived since last event-loop idle.
class GUdpReceiver : public QObject
{
	Q_OBJECT
public:
	
	// construction / destruction
	
	GUdpReceiver( QObject* parent = 0 );
	virtual ~GUdpReceiver();
	
	// configuration
	
	/// Starts receiving on specified port/interface (asynchronous)
	void startReceiving( quint16 port, const QHostAddress& address = QHostAddress::Any );
	/// Stops receiving
	void stopReceiving();
	/// Checks if recievies is currently receiving
	bool isReceiving() const;
	
	/// Sets packet timeout (max time between datagrams)
	void setPacketTimeout( int msec );
	
	/// Enables congestrion control
	void setCongestionEnabled( bool enabled ) { _congestion = enabled; }
	
	/// Queries congestion control state
	bool isCongetsionEnabled() const { return _congestion; }
	
	/// Checks for incoming datagram and processes it. Non-blocking.
	void receiveDatagrams();
	
signals:

	/// Emitted when complete packet arrives
	void packetReceived( int packetId, const QByteArray& data );
	
	/// Emited in congestion control mode for each set of subsequently received packets
	void packetsReceived( const QQueue< QPair<quint16, QByteArray> >& packets );
	
protected slots:

	/// called by socket when datagarma arrives
	void datagramArrived();
	
	/// called by timer, to check old packets
	void checkForTimeouted();

private:
	
	/// incoming packet data
	struct _IncomingPacket
	{
		int datagramsInPacket;	///< Total number of datagrams
		int receivedDatagarams;	///< Number of alredy recievied datagrams
		QVector<QByteArray> data;	///< List of recieved datagrams. Unrecieved yet are null
		QDateTime lastSeen;		///< When packed has send any datagram. Ued to remove otdated
	};
	
	/// Incoming packets, by id
	QMap< quint16, _IncomingPacket >	_packets;
	
	QUdpSocket	_socket;		///< Socket
	int 		_timeout;		///< Packet timeout [msec]
	QTimer		_timer;			///< Timer triggering packet cleanup
	bool		_congestion;	///< Congestion-control enabled
	
	/// Packet queue used in congestion control
	QQueue< QPair<quint16, QByteArray> > _packetQueue;
	
	/// Handles incoming datagram
	void processDatagram();
	
	/// Handles completed packets
	void packetCompleted( int id, const _IncomingPacket& packet );

};




#endif // !GNETUDP_H

// eof
